﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Xml.Linq;

namespace LinqToOwin.Web.Labs.Data
{
  // http://graphical.weather.gov/xml/rest.php
  internal static class WeatherClient
  {
    private const string baseServiceUrl = "http://graphical.weather.gov/xml/sample_products/browser_interface/";
    private const string citiesLevel = "12";
    private const string weatherByCityQuery = "?whichClient=NDFDgenByDayMultiCities&citiesLevel=" + citiesLevel + "&format=12+hourly&numDays=1&Unit=e";
    private const string cityGridQuery = "?whichClient=LatLonListCities&listCitiesLevel=" + citiesLevel;
    private static readonly Uri weatherByCityServiceUrl = new Uri(baseServiceUrl + "ndfdBrowserClientByDay.php" + weatherByCityQuery, UriKind.Absolute);
    private static readonly Uri cityGridServiceUrl = new Uri(baseServiceUrl + "ndfdXMLclient.php" + cityGridQuery, UriKind.Absolute);
    private static readonly Lazy<Task<Dictionary<string, Tuple<decimal, decimal>>>> cityGridAsync = new Lazy<Task<Dictionary<string, Tuple<decimal, decimal>>>>(GetCityGridAsync, isThreadSafe: true);

    private static async Task<Dictionary<string, Tuple<decimal, decimal>>> GetCityGridAsync()
    {
      using (var client = new HttpClient())
      {
        var xml = await client.GetStringAsync(cityGridServiceUrl).ConfigureAwait(false);

        var document = XDocument.Parse(xml);

        var grid = (from pair in ((string)document.Root.Element("latLonList")).Split(' ')
                    let values = pair.Split(',')
                    where values.Length == 2
                    select new
                    {
                      Latitude = decimal.Parse(values[0]),
                      Longitude = decimal.Parse(values[1])
                    })
                    .ToList();

        var cities = (from pair in ((string)document.Root.Element("cityNameList")).Split('|')
                      let values = pair.Split(',')
                      where values.Length == 2
                      select new
                      {
                        City = values[0],
                        State = values[1]
                      })
                      .ToList();

        return grid.Zip(cities, (g, c) => new { g, c })
                   .ToDictionary(
                      pair => GetKey(pair.c.City, pair.c.State),
                      pair => Tuple.Create(pair.g.Latitude, pair.g.Longitude));
      }
    }

    internal static string GetKey(string city, string state)
    {
      return city + "|" + state;
    }

    public static async Task<IEnumerable<Weather>> GetCurrentWeather(ICollection<string> cities)
    {
      if (cities == null || cities.Count == 0 || cities.All(string.IsNullOrWhiteSpace))
      {
        return null;
      }

      var cityGrid = await cityGridAsync.Value.ConfigureAwait(false);

      var queryStringParameters = "&startDate=" + DateTime.UtcNow.ToString("YYYY-MM-DD");

      using (var client = new HttpClient())
      {
        var xml = await client.GetStringAsync(weatherByCityServiceUrl.AbsoluteUri + queryStringParameters).ConfigureAwait(false);

        return Weather.Parse(xml, cities, cityGrid);
      }
    }
  }
}